/*
 *  Copyright 2009-2011 Ricardo Engel
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.mookt.unit.currency;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Collections;
import java.util.Currency;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Provider for available currencies using a file.
 * 
 * <p>
 * This reads the first resource named
 * {@code /org/mookt/unit/currency/CurrencyData.csv} on the classpath.
 * </p>
 * 
 * @author Stephen Colebourne
 * @author Ricardo Engel
 */
class ResourceFileProvider implements Provider {

    /** Map of registered currencies by text code. */
    private final ConcurrentMap<String, CurrencyUnit> currenciesByCode = new ConcurrentHashMap<String, CurrencyUnit>();

    /** Map of registered currencies by numeric code. */
    private final ConcurrentMap<Short, CurrencyUnit> currenciesByNumericCode = new ConcurrentHashMap<Short, CurrencyUnit>();

    /** Regex format for the csv line. */
    private static final Pattern REGEX_LINE = Pattern
            .compile("([A-Z]{3}),(-1|[0-9]{1,3}),(-1|0|1|2|3),([A-Z]*)#?.*");

    /**
     * Registers all the currencies known by this provider.
     * 
     * <p>
     * This reads the first resource named
     * '/org/mookt/unit/currency/CurrencyData.csv' on the classpath.
     * </p>
     * 
     * @throws Exception if an error occurs
     */
    public ResourceFileProvider() throws Exception {
        final InputStream in = getClass().getResourceAsStream(
                "/org/mookt/unit/currency/CurrencyData.csv");
        if (in == null) {
            throw new FileNotFoundException(
                    "Data file /org/mookt/unit/currency/CurrencyData.csv not found");
        }
        final BufferedReader reader = new BufferedReader(new InputStreamReader(
                in, "UTF-8"));
        String line;
        while ((line = reader.readLine()) != null) {
            final Matcher matcher = REGEX_LINE.matcher(line);
            if (matcher.matches()) {
                final Set<String> countryCodes = new HashSet<String>();
                final String countryStr = matcher.group(4);
                final String currencyCode = matcher.group(1);
                if (countryStr.length() % 2 == 1) {
                    continue; // invalid line
                }
                for (int i = 0; i < countryStr.length(); i += 2) {
                    countryCodes.add(countryStr.substring(i, i + 2));
                }
                final short numericCode = Short.parseShort(matcher.group(2));
                final byte decimalPlaces = Byte.parseByte(matcher.group(3));
                String symbol;
                try {
                    symbol = Currency.getInstance(currencyCode).getSymbol();
                } catch (final IllegalArgumentException ex) {
                    symbol = "\u00a4";
                }
                final CurrencyUnit currency = CurrencyUnit.Factory.create(
                        symbol, decimalPlaces, currencyCode, numericCode,
                        countryCodes);
                currenciesByCode.putIfAbsent(currencyCode, currency);
                if (numericCode >= 0) {
                    currenciesByNumericCode.putIfAbsent(numericCode, currency);
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mookt.unit.currency.CurrencyUnitProvider#get(java.lang.String)
     */
    public CurrencyUnit get(final String code) {
        return currenciesByCode.get(code);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mookt.unit.currency.CurrencyUnitProvider#get(short)
     */
    public CurrencyUnit get(final short numericCode) {
        return currenciesByNumericCode.get(numericCode);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mookt.unit.currency.CurrencyUnitProvider#getAll()
     */
    public Collection<CurrencyUnit> getAll() {
        return Collections.unmodifiableCollection(currenciesByCode.values());
    }

}
